using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Gj.DarkLoong;
using Gj.Galaxy.Logic;
using Gj.Galaxy.Network;
using Gj.ILRuntimeExt;
using Gj.TapTap;
using SimpleJSON;
using Unity.Services.Core;
// using TapTap.Bootstrap;
using UnityEngine;
using UnityEngine.SceneManagement;
using WebSocketSharp;
using Client = Gj.Galaxy.Client;

namespace Gj
{
    public class BundleLoad
    {
        public string Bundle;
        public string Variant;
        public string Namespace = "";

        public int max = -1;
    }

    public enum LoadProcess
    {
        None,
        Process,
        Finish,
        Fail,
    }

    public enum FlowProcess
    {
        None,
        Process,
        ReProcess,
        Finish,
    }

    public class History
    {
        internal struct U
        {
            public string Scene;
            public JSONObject Param;
        }
        private static List<U> sceneList = new List<U>();
        public static string CurrentScene = "";
        private static int TargetIndex = -1;
        private static bool wait = false;
        private static U PrevScene = new U { Scene = "" };

        internal static JSONObject InitScene(string scene)
        {
            if (TargetIndex == -1)
            {
                // 第一次初始化
                TargetIndex = 0;
                sceneList.Add(new U { Scene = scene });
            }
            var u = sceneList[TargetIndex];
            if (u.Scene != scene)
            {
                LogTools.Error("Scene no match history:" + u.Scene + "->" + scene);
            }

            wait = false;

            CurrentScene = scene;
            return u.Param;
        }

        public static string Prev()
        {
            return PrevScene.Scene;
        }

        public static void Jump(string scene, JSONObject param = null)
        {
            if (wait) return;
            if (scene == CurrentScene) return;
            LogTools.Process("Scene {0} jump", scene);
            var u = new U { Scene = scene, Param = param };
            Project.ProjectUpdate.Queue.Enqueue(async () =>
            {
                Resource.PopBundle();
                Resource.PushBundle();
                sceneList.Add(u);
                wait = true;
                PrevScene = sceneList[TargetIndex];
                TargetIndex = sceneList.Count - 1;
                Project.ChangeScene(scene, param);
                AnalyticsManager.Event("jumpScene", new Dictionary<string, object> { { "scene", scene }, { "param", param != null ? param.ToString() : "null" } });
            });
        }

        public static void Replace(string scene, JSONObject param = null)
        {
            if (wait) return;
            wait = true;
            var u = sceneList[TargetIndex];
            u.Param = param;
            u.Scene = scene;
            Game.single.ClearLoop();
            SceneManager.LoadSceneAsync(u.Scene);
            LogTools.Process("Scene {0} replace", u.Scene);
            AnalyticsManager.Event("replaceScene", new Dictionary<string, object> { { "scene", u.Scene }, { "param", param != null ? param.ToString() : "null" } });
        }

        public static void Back()
        {
            if (wait) return;
            U u;
            U front;
            if (TargetIndex == 0)
            {
                return;
            }
            Project.ProjectUpdate.Queue.Enqueue(async () =>
            {
                front = sceneList[TargetIndex];
                sceneList.RemoveAt(TargetIndex);
                Resource.PopBundle();

                PrevScene = front;
                wait = true;
                TargetIndex -= 1;
                u = sceneList[TargetIndex];
                Resource.PushBundle();
                Project.ChangeScene(u.Scene, u.Param);
                AnalyticsManager.Event("backScene", new Dictionary<string, object> { { "scene", u.Scene }, { "front", front.Scene } });
            });
        }
    }
    public class ProjectUpdate : GameManager
    {
        internal ConcurrentQueue<Func<Task>> Queue = new ConcurrentQueue<Func<Task>>();
        internal bool t1 = false;
        internal bool t2 = false;

        internal AssetBundle currentSceneBundle;

        public void OnDestroy()
        {
        }

        public void OnInit()
        {
        }

        public void OnPage(BasePage page)
        {
            LogTools.Log($"onPage {History.CurrentScene}");
            if (History.CurrentScene == "Main")
            {
                // if (!LeanCloud.Auth.IsLogin())
                // {
                //     Queue.Enqueue(async () =>
                //     {
                //         try
                //         {
                //             LogTools.Log("LeanCloud login");
                //             await LeanCloud.LeanCloud.Login(AuthConnect.player);
                //             // var data = new JSONObject();
                //             // data["time"] = 1;
                //             // data["user"] = "user";
                //             // data["setting"] = new JSONObject();
                //             // data["setting"]["test"] = true;
                //             // page.Trigger(SystemEvent.SaveRecord, data);
                //         }
                //         catch (Exception e)
                //         {
                //             throw e;
                //         }
                //     });
                // }
#if UNITY_EDITOR
                if (!DarkLoong.Auth.IsLogin())
                {
                    Debug.LogError("notLogin");
                    Queue.Enqueue(async () =>
                    {
                        var token = Manager.settings.userToken != "" ? Manager.settings.userToken : CacheTools.GetVal("userToken", "");
                        if (!string.IsNullOrEmpty(token))
                        {
                            await DarkLoong.DarkLoong.Token(token, (info) =>
                            {
                                AuthConnect.player.Inited(info);
                                AuthConnect.player.Id = AuthConnect.player.Instance["id"];
                                AuthConnect.player.UserId = AuthConnect.player.Instance["userId"];
                                AuthConnect.Token = DarkLoong.Auth.SessionToken;
                            }, (err) =>
                            {
                                
                            });
                        }
                        // AuthConnect.player.Instance = new JSONObject();
                        // AuthConnect.player.Instance["platform"] = "local";
                        // AuthConnect.player.Instance["username"] = "gaojie";
                        // AuthConnect.player.Instance["password"] = "";
                        // AuthConnect.player.UserId = "extendid";
                        // DarkLoong.DarkLoong.Login(SocialManager.AuthType._, "local", AuthConnect.player);
                        // var data = new JSONObject();
                        // data["time"] = 1;
                        // data["user"] = "user";
                        // data["setting"] = new JSONObject();
                        // data["setting"]["test"] = true;
                        // page.Trigger(SystemEvent.SaveRecord, data);
                    });
                }
#endif
            }
        }

        public void UpdateClient(bool success)
        {
            t1 = false;
            t2 = false;
            Queue.Enqueue(async () =>
            {
                if (success)
                {
                    AnalyticsManager.Event("clientConnectSuccess");
                }
                else
                {
                    AnalyticsManager.Event("clientConnectFail");
                }
                Project.NoticeGameServer(success);
            });
        }

        public void ReconnectClient(int tt)
        {
            // t1发了，t2就不发了
            switch (tt)
            {
                case 1:
                    t1 = true;
                    break;
                case 2:
                    t2 = true;
                    if (t1) return;
                    break;
                default:
                    return;
            }
            Queue.Enqueue(async () =>
            {
                Project.ReconnectingGameServer();
            });

        }

        public void CenterClient(bool success)
        {
            LogTools.Log("center reconnect " + t2 + ":" + t1 + "->" + success);
            if (t2 || t1)
            {
                // 断线后正常链接，触发重连逻辑
                t2 = !success;
                if (t1)
                {
                    return;
                }
                Queue.Enqueue(async () =>
                {
                    Project.ReconnectGameServer(success);
                });
            }
        }

        public void Send(SystemValue key)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key);
                }
            });
        }
        public void Send(SystemValue key, JSONObject obj)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key, obj);
                }
            });
        }
        public void Send(SystemValue key, JSONArray obj)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key, obj);
                }
            });
        }
        public void Send(SystemValue key, object obj)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key, obj);
                }
            });
        }
        public void Send(SystemValue key, string obj)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key, obj);
                }
            });
        }
        public void Send(SystemValue key, float obj)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key, obj);
                }
            });
        }
        public void Send(SystemValue key, bool obj)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key, obj);
                }
            });
        }
        public void Send(SystemValue key, int obj)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key, obj);
                }
            });
        }
        public void Send(SystemValue key, GameObject obj)
        {
            Queue.Enqueue(async () =>
            {
                if (Project.currentPage != null)
                {
                    Project.currentPage.Send(key, obj);
                }
            });
        }

        public void WaitSeconds(float time, Action CB)
        {
            Queue.Enqueue(async () =>
            {
                Game.single.WaitSeconds(time, CB);
            });
        }

        public LoopTimer GlobalLoop(float time, Action CB)
        {
            var t = Game.single.GlobalLoop(time, null);
            Queue.Enqueue(async () =>
            {
                t.Restart(CB);
            });
            return t;
        }
        public void ReconnectClient(int tt, bool success)
        {
            LogTools.Log("reconnect " + tt + "->" + success);
            switch (tt)
            {
                case 1:
                    t1 = !success;
                    if (success)
                    {
                        if (t2)
                        {
                            return;
                        }
                    }
                    break;
                case 2:
                    t2 = !success;
                    if (success)
                    {
                        if (t1)
                        {
                            return;
                        }
                    }
                    break;
                default:
                    return;
            }
            Queue.Enqueue(async () =>
            {
                Project.ReconnectGameServer(success);
            });
        }

        public void LogicSuccess(GamePlayer player, string nickname)
        {
            Queue.Enqueue(async () =>
            {
                AnalyticsManager.Event("loginSuccess");
                TapTap.TapTap.Login(player);
                if (Project.currentPage != null)
                {
                    var info = player.Instance;
                    Project.currentPage.Send(SystemMessage.LoginSuccess, info);
                    Project.currentPage.Trigger(SystemEvent.GetRealStatus);
                }
            });
        }

        public void LoadScene(string scene, AssetBundle bundle)
        {
            Queue.Enqueue(async () =>
            {
                var e = LoadSceneInline(scene, bundle);
                while (true)
                {
                    var b = await e.MoveNextAsync();
                    if (!b)
                    {
                        break;
                    }
                }

            });
        }

        private async IAsyncEnumerator<AsyncOperation> LoadSceneInline(string scene, AssetBundle bundle)
        {
            if (bundle != null)
            {
                var scenes = bundle.GetAllScenePaths();
                scene = scenes[0];
            }
            if (currentSceneBundle != null)
            {
                currentSceneBundle.Unload(false);
                currentSceneBundle = null;
            }
            LogTools.Log(scene + "->" + (bundle != null));

            var a = SceneManager.LoadSceneAsync(Path.GetFileNameWithoutExtension(scene), LoadSceneMode.Single);
            if (a != null)
            {
                a.completed += (c) =>
                {
                    // if (bundle != null)
                    // {
                    // if (currentScene.IsValid())
                    // {
                    //     yield return SceneManager.UnloadSceneAsync(currentScene);
                    // }
                    //     currentScene = SceneManager.GetSceneByPath(scene);
                    //     SceneManager.SetActiveScene(currentScene);
                    // }
                    currentSceneBundle = bundle;
                };
            }
            yield return a;
        }

        public void LoadBundle(BundleLoad[] list, Action<AssetBundle> callback, Action<float> notice)
        {
            Queue.Enqueue(async () =>
            {
                Project.LoadBundle(list, callback, notice);
            });
        }

        public async Task OnUpdate()
        {
            while (Queue.TryDequeue(out var action))
            {
                await action();
            }
        }
    }
    // 热更逻辑整合类
    public class Project
    {
        public static string MainProject = "game";
        public static string DefaultScene = "main";
        internal static BasePage currentPage = null;

        public const string environmentFile = "settings.json";
        public static EnvironmentSetting EnvironmentSetting = null;
        public const string userFile = "user_settings.json";
        public static UserSetting UserSetting = null;

        public const int End = 5;
        public const int RecoonectTimes = 7;
        public static bool init = false;
        private static bool debug = false;
        private static LoopTimer t;

        private static FlowProcess flowFinish = FlowProcess.None;
        private static LoadProcess appFinish = LoadProcess.None;
        private static LoadProcess socialFinish = LoadProcess.None;
        private static LoadProcess projectFinish = LoadProcess.None;
        private static LoadProcess connectFinish = LoadProcess.None;
        private static Action finishAction;
        private static Action connectAction;

        internal static Dictionary<string, Uri> domainUriMap = null;

        public static bool Debug
        {
            get { return debug; }
            set
            {
                debug = value;
                AnalyticsManager.Ignore = debug;
                LogTools.debug = Editor || debug;
                LogTools.Info("Change Debug status:" + debug);
            }
        }

        public static ChannelInfo Channel
        {
            get { return ChannelManager.Info(); }
        }

#if UNITY_EDITOR
        public static bool Editor = true;
#else
        public static bool Editor = false;
#endif

        public static bool Single;

        public static BasePage CurrentPage
        {
            get { return currentPage; }
        }

        private static int initProcess = 0;

        public static bool FinishInit
        {
            get { return initProcess == End; }
        }

        internal static List<string> projects = new List<string>();
        private static List<string> sceneList;
        internal static ProjectUpdate ProjectUpdate = new ProjectUpdate();

        public static void Api(Task<JSONObject> task, Action<JSONObject, Action<bool, string>> format, Action<bool, string> cb)
        {
            ProjectUpdate.Queue.Enqueue(async () =>
            {
                var response = await task;
                format(response, cb);
            });
        }
        public static void Api(Task<JSONObject> task, Action<JSONObject, Action<JSONObject, string>> format, Action<JSONObject, string> cb)
        {
            ProjectUpdate.Queue.Enqueue(async () =>
            {
                var response = await task;
                format(response, cb);
            });
        }
        public static void Api(Task<JSONObject> task, Action<JSONObject, Action<JSONArray, string>> format, Action<JSONArray, string> cb)
        {
            ProjectUpdate.Queue.Enqueue(async () =>
            {
                var response = await task;
                format(response, cb);
            });
        }

        public static async Task Init(string scene = "")
        {
            if (init)
            {
                return;
            }
            LogTools.Log("Project start " + scene);

            initProcess = 0;
            init = true;

            Base();
            Game.Init();
            Game.single.RegisterManager(ProjectUpdate);
            // await UnityCloud.UnityCloud.Init();
            // await LeanCloud.LeanCloud.Init();

            if (scene != "")
            {
                DefaultScene = scene;
            }
            t = Game.single.GlobalLoop(1.90f, () =>
            {
                if (!Game.single.CheckNetwork())
                {
                    currentPage.Send(SystemMessage.NetworkOffline, Time.time);
                }
                else if (flowFinish == FlowProcess.Process)
                {
                    Flow(finishAction);
                }
            });
            t.Stop();

            if (Game.DefaultCustomIpSettings())
            {
                Client.BindCustom(Game.single);
            }
            ChannelManager.Init();
            while (true)
            {
                if (ChannelManager.Inited)
                {
                    break;
                }
                await Task.Delay(1);
            }

            await ChannelInitedHandle();
        }

        private static async Task ChannelInitedHandle()
        {
            var channelInfo = ChannelManager.Info();
            if (channelInfo.social.Contains(SocialManager.Platform.TapCn))
            {
                TapTap.TapTap.NeedInit = true;
                await TapTap.TapTap.Init(channelInfo);
            }
            // AutoDarkLoong();

            // TapTap.Im.Register(new Prefix(){Key="global", IsGlobal = true});
            // TapTap.Im.Register(new Prefix(){Key="identity", IsGroup = true});
            // TapTap.Im.Register(new Prefix(){Key="friend", IsPrivate = true, NeedHistory = true});
            // TapTap.Im.Register(new Prefix(){Key="legion", IsGroup = true, NeedHistory = true});
            // TapTap.Im.Register(new Prefix(){Key="team", IsTmp = true});
            // TapTap.Im.Register(new Prefix(){Key="room", IsTmp = true});


            if (SocialManager.CanAuth(SocialManager.Platform.TapCn) ||
                SocialManager.CanAuth(SocialManager.Platform.TapCn))
            {
                await TapTap.TapTap.Init(Project.Channel);
            }

            Steam.Steam.Init(channelInfo);

            Register.Init();
            AdManager.IsChina = channelInfo.isChina;
            AdManager.BindAllow(channelInfo.ad);
            SocialManager.BindAllow(channelInfo.social);
            AnalyticsManager.BindAllow(channelInfo.analytics);

            Game.single.StartCoroutine(AdManager.Init());
            AnalyticsManager.Init();
            AnalyticsManager.RegisterDefault("Channel", channelInfo.channel);

            // 初始化debug状态
            Debug = String.IsNullOrEmpty(channelInfo.channel) ? Resource.Settings.Debug : channelInfo.debug;
            // 初始化single模式
            Single = String.IsNullOrEmpty(channelInfo.channel) ? Resource.Settings.Single : channelInfo.single;
            LogTools.Log("Single:" + Resource.Settings.Single + " - " + channelInfo.single + " - " + Single);

            BindGameServerEvent();
        }

        public static void Base()
        {
            LoadEnvironmentInfo();
            LoadUserInfo();
            if (EnvironmentSetting != null)
            {
                LogTools.Log(EnvironmentSetting.gameLink);
                Manager.SetDomain(EnvironmentSetting.gameLink);
            }
        }

        public static void SwitchServer(string node)
        {
            Manager.SetDomain(EnvironmentSetting.gameLink);
            Client.Close();
        }

        public static string Environment()
        {
            return EnvironmentSetting != null ? EnvironmentSetting.environment : "";
        }

        public static void Reconnect()
        {
            Client.Reconnect();
            Client.ReconnectCenter();
        }

        public static void Flow(Action afterInit, bool force = false)
        {
            LogTools.Log("init{0}->{1}->{2}->{3}", init, appFinish, projectFinish, socialFinish);
            if (!init)
            {
                if (flowFinish == FlowProcess.Finish)
                {
                    afterInit?.Invoke();
                }
                return;
            }

            if (appFinish == LoadProcess.Finish && projectFinish == LoadProcess.Finish &&
                socialFinish == LoadProcess.Finish && connectFinish == LoadProcess.Finish)
            {
                afterInit();
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.InitAppKey, "flowFinish");

                    currentPage.Send(SystemMessage.InitApp, initProcess);
                }
                flowFinish = FlowProcess.Finish;
                return;
            }

            if (flowFinish == FlowProcess.Process)
            {
                flowFinish = FlowProcess.ReProcess;
            }
            else
            {
                finishAction = afterInit;
                flowFinish = FlowProcess.Process;
            }

            if (appFinish == LoadProcess.None) InitApp();
            else if (projectFinish == LoadProcess.None) InitProject();
            if (socialFinish == LoadProcess.None) InitSocial();
            if (connectFinish == LoadProcess.None)
            {
                if (!Single)
                {
                    AutoIp();
                }
                else
                {
                    initProcess += 1;
                    currentPage.Send(SystemMessage.InitAppKey, "connectSingle");
                    connectFinish = LoadProcess.Finish;
                }
            }
        }

        public static void AutoDarkLoong()
        {
            Game.single.GetAddressList(DarkLoong.Api.API_DOMAIN, (u) =>
            {
                LogTools.Log("auto:" + DarkLoong.Api.API_DOMAIN + "->" + u.Length);
                DarkLoong.Api.AddIps(u);
            });
        }

        public static void AutoIp()
        {
            if (domainUriMap != null) return;
            domainUriMap = new Dictionary<string, Uri>();
            var s = Client.DefaultSettings();

            Uri gameDomain = new Uri("http://" + s.GameServerAddress);
            Uri centerDomain = new Uri("http://" + s.CenterServerAddress);

            // 用于本地快速切换不同环境，而不影响打包
            if (EnvironmentSetting != null)
            {
                Client.UpdateLink(EnvironmentSetting.gameLink, EnvironmentSetting.centerLink, EnvironmentSetting.protocol);
                gameDomain = new Uri("http://" + EnvironmentSetting.gameLink);
                centerDomain = new Uri("http://" + EnvironmentSetting.centerLink);
            }
            Game.single.GetAddress(gameDomain.Host, (u) =>
            {
                LogTools.Log("auto:" + gameDomain.Host + "->" + u);
                if (gameDomain.Host != centerDomain.Host)
                {
                    Game.single.GetAddress(centerDomain.Host, (u) =>
                    {
                        LogTools.Log("auto:" + centerDomain.Host + "->" + u);
                        domainUriMap[centerDomain.Host] = u;
                        Client.Connect();
                    });
                }
                else
                {
                    LogTools.Log("auto:" + centerDomain.Host + "->" + u);
                    Client.Connect();
                }
            });
        }

        public static void SetPage(BasePage page, bool runtime = true, string project = null)
        {
            currentPage = page;
            if (runtime)
            {
                if (project.IsNullOrEmpty()) project = MainProject;
                LogTools.Info("setPage:" + project + ".Page." + History.CurrentScene + "Page");
                Instance(project + ".Page." + History.CurrentScene + "Page", page);
                // todo 初始化
                if (TapTap.TapTap.ImSuccess)
                {
                    ProjectUpdate.Send(SystemMessage.ConnectChat);
                }
            }
            else
            {
                foreach (var manager in Game.single._managerList)
                {
                    manager.OnPage(page);
                }
            }
        }
        internal static void ChangeScene(string scene, JSONObject param)
        {
            if (currentPage != null)
            {
                currentPage.Send(SystemMessage.LoadingScene, 0);
            }
            Game.single.ClearLoop();

            var list = (BundleLoad[])StaticMethod(MainProject + ".Global", "OnScene", scene, param);
            if (list == null)
            {
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.LoadedScene);
                }

                LogTools.Log("empty load scene" + scene);
                ProjectUpdate.LoadScene(scene, null);
                return;
            }
            ProjectUpdate.LoadBundle(list, (bundle) =>
            {
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.LoadedScene);
                }
                Localization.Instance.Update();
                LogTools.Log("list load scene" + scene + ":" + bundle);
                ProjectUpdate.LoadScene(scene, bundle && bundle.isStreamedSceneAssetBundle ? bundle : null);
            }, (process) =>
            {
                // 通知加载
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.LoadingScene, process);
                }
            });
        }

        private static void InitSocial()
        {
            socialFinish = LoadProcess.Process;
            SocialManager.Init(() =>
            {
                socialFinish = LoadProcess.Finish;
                initProcess += 1;

                StaticMethod(MainProject + ".Global", "OnSocial");
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.InitAppKey, "socialInit");
                    currentPage.Send(SystemMessage.InitApp, initProcess);
                }
            }, true);
        }

        private static void InitApp()
        {
            appFinish = LoadProcess.Process;
            if (currentPage != null)
            {
                currentPage.Send(SystemMessage.InitApp, initProcess);
            }
            // var result = AndroidPermission.Has(PermissionDefine.ExternalStorageWrite);
            // LogTools.Log("Permission result:{0}->{1}", PermissionDefine.ExternalStorageWrite, result);
            // if (result == PermissionResult.Try)
            // {
            //     AndroidPermission.BindCallback(PermissionDefine.ExternalStorageWrite, (rr) =>
            //     {
            //         LogTools.Log("Permission callback:{0}", PermissionDefine.ExternalStorageWrite);
            //         InitApp();
            //     });
            //     return;
            // }
            // else if (result == PermissionResult.Again)
            // {
            //     currentPage.Send(SystemMessage.RequestPermission, AndroidPermission.ToPermission(PermissionDefine.ExternalStorageWrite));
            //     return;
            // }
            // else if (result == PermissionResult.Refuse)
            // {
            //     currentPage.Send(SystemMessage.RefusePermission, AndroidPermission.ToPermission(PermissionDefine.ExternalStorageWrite));
            // }
            Game.single.StartCoroutine(
                Resource.InitApp((bool arg1, bool arg2, bool arg3) =>
                {
                    if (!arg3)
                    {
                        t.Resume();
                        //更新失败
                        if (currentPage != null)
                        {
                            currentPage.Send(SystemMessage.InitAppKey, "appFail");
                        }

                        appFinish = LoadProcess.Fail;
                        Flow(finishAction);
                        AnalyticsManager.Event("appFail");
                        return;
                    }
                    LogTools.Info("app status:" + arg1);
                    LogTools.Info("resource status:" + arg2);
                    if (arg1)
                    {
                        AnalyticsManager.Event("appNeedUpgrade");
                        // 通知app更新
                        if (currentPage != null)
                        {
                            currentPage.Send(SystemMessage.AppNeedUpgrade);
                        }
                    }
                    else if (arg2)
                    {
                        AnalyticsManager.Event("resourceDownload");
                        initProcess += 1;
                        if (currentPage != null)
                        {
                            currentPage.Send(SystemMessage.InitAppKey, "resourceDownload");
                            currentPage.Send(SystemMessage.InitApp, initProcess);
                        }
                        // 资源更新
                        InitResource();
                    }
                    else
                    {
                        AnalyticsManager.Event("appFinish");
                        initProcess += 2;
                        if (currentPage != null)
                        {
                            currentPage.Send(SystemMessage.InitAppKey, "appFinish");
                            currentPage.Send(SystemMessage.InitApp, initProcess);
                        }

                        appFinish = LoadProcess.Finish;
                        InitProject();
                    }
                })
            );
        }

        private static void InitResource()
        {
            int max = -1;
            // 资源更新，重置project加载信息
            projects.Clear();
            Game.single.StartCoroutine(
                Resource.Update((int i, string bundle) =>
                {
                    if (max == -1) max = i;
                    // 更新进度，从大到小，0更新完成
                    if (i == 0)
                    {
                        AnalyticsManager.Event("resourceFinish");
                        initProcess += 1;
                        if (currentPage != null)
                        {
                            currentPage.Send(SystemMessage.InitAppKey, "resourceFinish");
                            currentPage.Send(SystemMessage.InitApp, initProcess);
                        }
                        appFinish = LoadProcess.Finish;
                        InitProject();
                    }
                    else
                    {
                        // 通知资源更新进度
                        if (currentPage != null)
                        {
                            currentPage.Send(SystemMessage.InitResource, (float)(max - i) / max);
                        }
                    }
                })
            );
        }

        private static void InitProject()
        {
            projectFinish = LoadProcess.Process;
            // 通知更新完成，加载资源
            LoadProject(MainProject, (success) =>
            {
                if (!success)
                {
                    projectFinish = LoadProcess.Fail;
                    Flow(finishAction);
                    currentPage.Send(SystemMessage.InitAppKey, "projectFail");
                    AnalyticsManager.Event("projectFail");
                    return;
                }
                var list = (BundleLoad[])StaticMethod(MainProject + ".Global", "BaseBundle");
                if (list != null && list.Length > 0)
                {
                    ProjectUpdate.LoadBundle(list, (bundle) =>
                    {
                        projectFinish = LoadProcess.Finish;
                        InitProjectInline();
                        Localization.Instance.Update();
                    }, (process) =>
                    {
                        currentPage.Send(SystemMessage.LoadingProject, process);
                    });
                }
                else
                {
                    projectFinish = LoadProcess.Finish;
                    InitProjectInline();
                }
            });
        }

        private static void InitProjectInline()
        {
            AnalyticsManager.Event("projectSuccess");
            StaticMethod(MainProject + ".Global", "OnInit", null);
            // 通知开始服务器链接
            initProcess += 1;
            if (currentPage != null)
            {
                currentPage.Send(SystemMessage.InitAppKey, "projectFinish");
                currentPage.Send(SystemMessage.InitApp, initProcess);
            }
            Flow(finishAction);
        }

        internal static void NoticeGameServer(bool success)
        {
            if (success)
            {
                t.Stop();
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.ConnectSuccess);
                }

                if (connectAction != null)
                {
                    connectAction();
                    connectAction = null;
                }
                if (!FinishInit)
                {
                    // 通知链接成功
                    initProcess += 1;
                    connectFinish = LoadProcess.Finish;
                    StaticMethod(MainProject + ".Global", "OnConnectSuccess");
                    if (currentPage != null)
                    {
                        currentPage.Send(SystemMessage.InitApp, initProcess);
                        currentPage.Send(SystemMessage.InitAppKey, "connectSuccess");
                    }
                    Flow(finishAction);
                }
            }
            else
            {
                connectFinish = LoadProcess.Fail;
                t.Resume();
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.ConnectFail);
                }

                if (!FinishInit && currentPage != null)
                {
                    currentPage.Send(SystemMessage.InitAppKey, "connectFail");
                }
                var s = Client.DefaultSettings();

                Uri gameDomain = new Uri("http://" + s.GameServerAddress);

                // 用于本地快速切换不同环境，而不影响打包
                if (EnvironmentSetting != null)
                {
                    gameDomain = new Uri("http://" + EnvironmentSetting.gameLink);
                }
                Game.single.GetServiceStatus(gameDomain.Host, (u) =>
                {
                    if (currentPage != null)
                    {
                        currentPage.Send(SystemMessage.ServiceStatus, u);
                    }
                });

                LogTools.Error("Server connect fail");
            }
        }

        internal static void ReconnectingGameServer()
        {
            if (currentPage != null)
            {
                currentPage.Send(SystemMessage.Reconnecting);
            }
        }

        internal static void ReconnectGameServer(bool success)
        {
            if (success)
            {
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.ReconnectSuccess);
                }

                if (connectAction != null)
                {
                    connectAction();
                    connectAction = null;
                }
                if (!FinishInit)
                {
                    // 通知链接成功
                    initProcess += 1;
                    connectFinish = LoadProcess.Finish;
                    StaticMethod(MainProject + ".Global", "OnConnectSuccess");
                    if (currentPage != null)
                    {
                        currentPage.Send(SystemMessage.InitApp, initProcess);
                        currentPage.Send(SystemMessage.InitAppKey, "connectSuccess");
                    }
                    Flow(finishAction);
                }

            }
            else
            {
                connectFinish = LoadProcess.Fail;
                t.Resume();
                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.ReconnectFail);
                }

                if (!FinishInit && currentPage != null)
                {
                    currentPage.Send(SystemMessage.InitAppKey, "connectFail");
                }

                LogTools.Error("Server connect fail");
            }
        }

        private static void BindGameServerEvent()
        {
            Client.Listener.OnConnectEvent += (bool success) =>
            {
                LogTools.Info("[ SOCKET ] connect:" + success);
                ProjectUpdate.UpdateClient(success);
            };
            Client.CenterListener.OnConnectEvent += (success) =>
            {
                LogTools.Info("[ SOCKET ] Comet connect:" + success);
                ProjectUpdate.CenterClient(success);
            };
            Client.Listener.OnReconnectingEvent += () =>
            {
                LogTools.Info("[ SOCKET ] reconnecting");
                ProjectUpdate.ReconnectClient(1);
            };
            Client.CenterListener.OnReconnectingEvent += () =>
            {
                LogTools.Info("[ SOCKET ] Comet reconnecting");
                ProjectUpdate.ReconnectClient(2);
            };
            Client.Listener.OnReconnectEvent += (bool success) =>
            {
                LogTools.Info("[ SOCKET ] reconnect:" + success);
                ProjectUpdate.ReconnectClient(1, success);
            };
            Client.CenterListener.OnReconnectEvent += (bool success) =>
            {
                LogTools.Info("[ SOCKET ] Comet reconnect:" + success);
                ProjectUpdate.ReconnectClient(2, success);
            };
            AuthConnect.Listener.OnLogin += () =>
            {
                LogTools.Info("[ SOCKET ] Other Login");
                if (currentPage != null)
                {
                    ProjectUpdate.Send(SystemMessage.AuthOtherLogin);
                }
            };
            AuthConnect.Listener.OnInvitedFriend += (string userId) =>
            {
                LogTools.Info("[ SOCKET ] Friend Invited:{0}", userId);
                if (currentPage != null)
                {
                    ProjectUpdate.Send(SystemMessage.AuthInvitedFriend, userId);
                }
            };
            AuthConnect.Listener.OnConfirmedFriend = (userId, ok) =>
            {
                LogTools.Info("[ SOCKET ] Friend Confirmed:{0} {1}", userId);
                if (currentPage != null)
                {
                    ProjectUpdate.Send(ok ? SystemMessage.AuthConfirmedFriend : SystemMessage.AuthRefuseFriend, userId);
                }
            };
            AuthConnect.Listener.OnEventNoticeEvent += (t, values) =>
            {
                LogTools.Info("[ SOCKET ] Event Notice: {0}", t, values.Length);
                if (currentPage != null)
                {
                    var info = new JSONObject();
                    info["target"] = t;
                    var v = new JSONArray();
                    for (var i = 0; i < values.Length; i++)
                    {
                        v.Add(values[i]);
                    }
                    info["values"] = v;
                    ProjectUpdate.Send(SystemMessage.EventNotice, info);
                }
            };
            SceneConnect.Listener.OnInvitedTeamEvent += (string userId, string token) =>
            {
                LogTools.Info("[ SOCKET ] Team Invited:{0}", token);
                if (currentPage != null)
                {
                    var info = new JSONObject();
                    info.Add("user_id", userId);
                    info.Add("token", token);
                    ProjectUpdate.Send(SystemMessage.SceneInvitedTeam, info);
                }
            };
            SceneConnect.Listener.OnJoinedRoomEvent += (token, number) =>
            {
                LogTools.Info("[ SOCKET ] Game Can Join:{0}, {1}", token, number);
                if (currentPage != null)
                {
                    var info = new JSONObject();
                    info.Add("number", number);
                    info.Add("token", token);
                    ProjectUpdate.Send(SystemMessage.SceneJoinRoom, info);
                }
            };
            TeamConnect.Listener.OnConnectEvent += (bool success) =>
            {
                if (currentPage != null)
                {
                    if (success)
                    {
                        ProjectUpdate.Send(SystemMessage.TeamConnectSuccess, TeamConnect.Token);
                    }
                    else
                    {
                        ProjectUpdate.Send(SystemMessage.TeamConnectFail);
                    }
                }
            };
            TeamConnect.Listener.OnKickPlayerEvent += (TeamPlayer player) =>
            {
                if (currentPage != null)
                {
                    var info = (JSONObject)player.Instance;
                    info.Add("isMaster", player.Master);
                    //                    info.Add("location", player.Location);
                    info.Add("isReady", player.Status);
                    info.Add("info", player.Info);
                    ProjectUpdate.Send(SystemMessage.TeamKickPlayer, info);
                }
            };
            TeamConnect.Listener.OnJoinPlayerEvent += (TeamPlayer player) =>
            {
                if (currentPage != null)
                {
                    var info = (JSONObject)player.Instance;
                    info.Add("isMaster", player.Master);
                    //                    info.Add("location", player.Location);
                    info.Add("isReady", player.Status);
                    info.Add("info", player.Info);
                    ProjectUpdate.Send(SystemMessage.TeamJoinPlayer, info);
                    LogTools.Log("Team Join last:{0}=>{1}", player, info);
                }
            };
            TeamConnect.Listener.OnLeavePlayerEvent += (TeamPlayer player) =>
            {
                if (currentPage != null)
                {
                    var info = (JSONObject)player.Instance;
                    info.Add("isMaster", player.Master);
                    //                    info.Add("location", player.Location);
                    info.Add("isReady", player.Status);
                    info.Add("info", player.Info);
                    ProjectUpdate.Send(SystemMessage.TeamLeavePlayer, info);
                }
            };
            TeamConnect.Listener.OnJoinLobbyEvent += (int people) =>
            {
                ProjectUpdate.Send(SystemMessage.TeamJoinLobby, people);
            };
            TeamConnect.Listener.OnLeaveLobbyEvent += () =>
            {
                ProjectUpdate.Send(SystemMessage.TeamLeaveLobby);
            };
            TeamConnect.Listener.OnReadyPlayerEvent += (TeamPlayer player) =>
            {
                if (currentPage != null)
                {
                    var info = (JSONObject)player.Instance;
                    info.Add("isMaster", player.Master);
                    //                    info.Add("location", player.Location);
                    info.Add("isReady", player.Status);
                    info.Add("info", player.Info);
                    ProjectUpdate.Send(SystemMessage.TeamReadyPlayer, info);
                }
            };
            TeamConnect.Listener.OnChangeInfoPlayerEvent += (TeamPlayer player) =>
            {
                if (currentPage != null)
                {
                    var info = (JSONObject)player.Instance;
                    info.Add("isMaster", player.Master);
                    //                    info.Add("location", player.Location);
                    info.Add("isReady", player.Status);
                    info.Add("info", player.Info);
                    ProjectUpdate.Send(SystemMessage.TeamChangeInfoPlayer, info);
                }
            };
            TeamConnect.Listener.OnChangeInfoEvent += (JSONObject info) =>
            {
                if (currentPage != null)
                {
                    ProjectUpdate.Send(SystemMessage.TeamChangeInfo, info);
                }
            };
            TeamConnect.Listener.OnLeaveEvent += () =>
            {
                if (currentPage != null)
                {
                    ProjectUpdate.Send(SystemMessage.TeamLeave);
                }
            };

            // 监听room事件 - 通过RoomService.Listener访问
            if (ConnectManager.RoomService != null &&
                ConnectManager.RoomService.Listener is CombinedP2PListener combinedListener)
            {
                combinedListener.OnConnectEvent += (string reason) =>
                {
                    if (currentPage != null)
                    {
                        if (reason == "")
                        {
                            ProjectUpdate.Send(SystemMessage.RoomConnectSuccess);
                        }
                        else
                        {
                            ProjectUpdate.Send(SystemMessage.RoomConnectFail, reason);
                        }
                    }
                };
                combinedListener.OnKickPlayerEvent += (GamePlayer player) =>
                {
                    if (currentPage != null)
                    {
                        var info = (JSONObject)player.Instance;
                        info.Add("id", player.UserId);
                        info.Add("isMaster", player.Instance["isMaster"]);
                        //                    info.Add("location", player.Location);
                        info.Add("isReady", player.Instance["isReady"]);
                        // info.Add("info", player.Instance);
                        ProjectUpdate.Send(SystemMessage.RoomKickPlayer, info);
                    }
                };
                combinedListener.OnJoinPlayerEvent += (GamePlayer player) =>
                {
                    if (currentPage != null)
                    {
                        var info = (JSONObject)player.Instance;
                        info.Add("id", player.UserId);
                        info.Add("isMaster", player.Instance["isMaster"]);
                        //                    info.Add("location", player.Location);
                        info.Add("isReady", player.Instance["isReady"]);
                        // info.Add("info", player.Instance);
                        ProjectUpdate.Send(SystemMessage.RoomJoinPlayer, info);
                        LogTools.Log("Team Join last:{0}=>{1}", player, info);
                    }
                };
                combinedListener.OnLeavePlayerEvent += (GamePlayer player) =>
                {
                    if (currentPage != null)
                    {
                        var info = (JSONObject)player.Instance;
                        info.Add("id", player.UserId);
                        info.Add("isMaster", player.Instance["isMaster"]);
                        //                    info.Add("location", player.Location);
                        info.Add("isReady", player.Instance["isReady"]);
                        // info.Add("info", player.Instance);
                        ProjectUpdate.Send(SystemMessage.RoomLeavePlayer, info);
                    }
                };
                combinedListener.OnReadyPlayerEvent += (GamePlayer player) =>
                {
                    if (currentPage != null)
                    {
                        var info = (JSONObject)player.Instance;
                        info.Add("id", player.UserId);
                        info.Add("isMaster", player.Instance["isMaster"]);
                        //                    info.Add("location", player.Location);
                        info.Add("isReady", player.Instance["isReady"]);
                        // info.Add("info", player.Instance);

                        var ready = player.Instance["isReady"].AsBool;
                        UnityEngine.Debug.LogError(ready);
                        {
                            if (ready)
                            {
                                UnityEngine.Debug.LogError("ready");
                                ProjectUpdate.Send(SystemMessage.RoomReadyPlayer, info);
                            }
                            else
                            {
                                UnityEngine.Debug.LogError("cancel");
                                ProjectUpdate.Send(SystemMessage.RoomCancelPlayer, info);
                            }
                        }
                    }
                };
                combinedListener.OnChangePlayerEvent += (GamePlayer player) =>
                {
                    if (currentPage != null)
                    {
                        var info = (JSONObject)player.Instance;
                        info.Add("id", player.UserId);
                        info.Add("isMaster", player.Instance["isMaster"]);
                        //                    info.Add("location", player.Location);
                        info.Add("isReady", player.Instance["isReady"]);
                        // info.Add("info", player.Instance);
                        ProjectUpdate.Send(SystemMessage.RoomChangeInfoPlayer, info);
                    }
                };
                combinedListener.OnChangeRoomEvent += (RoomInfo info) =>
                {
                    if (currentPage != null)
                    {
                        var data = Encoding.UTF8.GetString(info.data);
                        var jo = JSONObject.Parse(data).AsObject;
                        jo["id"] = info.identity;
                        jo["running"] = info.running;
                        ProjectUpdate.Send(SystemMessage.RoomChangeInfo, jo);
                    }
                };
                combinedListener.OnFindRoomEvent += (RoomInfo info) =>
                {
                    if (currentPage != null)
                    {
                        var data = Encoding.UTF8.GetString(info.data);
                        var jo = JSONObject.Parse(data).AsObject;
                        jo["id"] = info.identity;
                        jo["running"] = info.running;
                        ProjectUpdate.Send(SystemMessage.RoomFind, jo);
                    }
                };
                combinedListener.OnGetClientAllEvent += (List<GamePlayer> players) =>
                {
                    if (currentPage != null)
                    {
                        var ja = new JSONArray();
                        foreach (var player in players)
                        {
                            var info = (JSONObject)player.Instance;
                            info.Add("id", player.UserId);
                            info.Add("isMaster", player.Instance["isMaster"]);
                            //                    info.Add("location", player.Location);
                            info.Add("isReady", player.Instance["isReady"]);
                            ja.Add(info);
                        }

                        ProjectUpdate.Send(SystemMessage.RoomPlayerList, ja);
                    }
                };
                combinedListener.OnGetRoomAllEvent += (List<RoomInfo> infos) =>
                {
                    if (currentPage != null)
                    {
                        var ja = new JSONArray();
                        foreach (var info in infos)
                        {
                            var data = Encoding.UTF8.GetString(info.data);
                            var jo = JSONObject.Parse(data).AsObject;
                            jo["id"] = info.identity;
                            jo["running"] = info.running;
                            ja.Add(jo);
                        }

                        ProjectUpdate.Send(SystemMessage.RoomList, ja);
                    }
                };
                combinedListener.OnStartRoomEvent += (RoomInfo info) =>
                {
                    if (currentPage != null)
                    {
                        var data = Encoding.UTF8.GetString(info.data);
                        var jo = JSONObject.Parse(data).AsObject;
                        jo["id"] = info.identity;
                        jo["running"] = info.running;
                        ProjectUpdate.Send(SystemMessage.RoomStart, jo);
                    }
                };
                combinedListener.OnKickEvent += () =>
                {
                    if (currentPage != null)
                    {
                        ProjectUpdate.Send(SystemMessage.RoomConnectFail, "kick");
                    }
                };
                combinedListener.OnDisbandEvent += () =>
                {
                    if (currentPage != null)
                    {
                        ProjectUpdate.Send(SystemMessage.RoomConnectFail, "disband");
                    }
                };
                combinedListener.OnCreateRoomEvent += (RoomInfo info) =>
                {
                    if (currentPage != null)
                    {
                        var data = Encoding.UTF8.GetString(info.data);
                        var jo = JSONObject.Parse(data).AsObject;
                        jo["id"] = info.identity;
                        jo["running"] = info.running;
                        ProjectUpdate.Send(SystemMessage.RoomCreate, jo);
                    }
                };
                combinedListener.OnJoinRoomEvent += (RoomInfo info) =>
                {
                    if (currentPage != null)
                    {
                        var data = Encoding.UTF8.GetString(info.data);
                        var jo = JSONObject.Parse(data).AsObject;
                        jo["id"] = info.identity;
                        jo["running"] = info.running;
                        ProjectUpdate.Send(SystemMessage.RoomJoin, jo);
                    }
                };
            }
            // TapTap.TapTap.OnConnectEvent += () =>
            // {
            //     if (currentPage != null)
            //     {
            //         ProjectUpdate.Send(SystemMessage.ConnectChat);
            //     }
            // };
            // TapTap.TapTap.OnReconnectEvent += () =>
            // {
            //     if (currentPage != null)
            //     {
            //         ProjectUpdate.Send(SystemMessage.ReconnectChat);
            //     }
            // };
            // TapTap.TapTap.OnConnectChatSuccessEvent += (string channel, int number) =>
            // {
            //     if (currentPage != null)
            //     {
            //         LogTools.Log("ConnectChatSuccess:"+channel+"->"+number);
            //         var message = new JSONObject();
            //         message["channel"] = channel;
            //         message["number"] = number;
            //         ProjectUpdate.Send(SystemMessage.ConnectChatSuccess, message);
            //     }
            // };
            // TapTap.TapTap.OnUpdateUnReadCountEvent += (string channel, int number) =>
            // {
            //     if (currentPage != null)
            //     {
            //         LogTools.Log("UnReadNumberChat:"+channel+"->"+number);
            //         var message = new JSONObject();
            //         message["channel"] = channel;
            //         message["number"] = number;
            //         ProjectUpdate.Send(SystemMessage.UnReadNumberChat, message);
            //     }
            // };
            // TapTap.TapTap.OnUpdateListEvent += (string prefix, TDSUser[] targets) =>
            // {
            //     if (currentPage != null)
            //     {
            //         LogTools.Log("OnUpdateListEvent:"+prefix+"->"+targets);
            //         var a = new JSONArray();
            //         foreach (var target in targets)
            //         {
            //             var tt = new JSONObject();
            //             tt["openId"] = Convert.ToString(target["openid"]);
            //             a.Add(tt);
            //         }
            //         switch (prefix)
            //         {
            //             case "friend":
            //                 ProjectUpdate.Send(SystemMessage.UpdateFriendChat, a);
            //                 break;
            //             default:
            //                 break;
            //         }
            //     }
            // };
            // TapTap.TapTap.OnListEvent += (string prefix, Conversation[] channels) =>
            // {
            //     if (currentPage != null)
            //     {
            //         LogTools.Log("OnListEvent:"+prefix+"->"+channels);
            //         var a = new JSONArray();
            //         foreach (var channel in channels)
            //         {
            //             var tt = new JSONObject();
            //             tt["icConnect"] = channel.IsConnect;
            //             tt["isOpen"] = channel.IsOpen;
            //             tt["channel"] = channel.Channel;
            //             tt["prefix"] = channel.Prefix.Key;
            //             tt["memberCount"] = channel.MemberCount;
            //             a.Add(tt);
            //         }
            //         switch (prefix)
            //         {
            //             case "global":
            //                 break;
            //             case "identity":
            //                 ProjectUpdate.Send(SystemMessage.ListChat, a);
            //                 break;
            //             default:
            //                 break;
            //         }
            //     }
            // };
            // TapTap.TapTap.OnMessageEvent += (string channel, TDSUser target, string content, bool isSelf) =>
            // {
            //     if (currentPage != null)
            //     {
            //         var message = new JSONObject();
            //         message["channel"] = channel;
            //         message["nickName"] = Convert.ToString(target["nickname"]);
            //         message["avatar"] = Convert.ToString(target["avatar"]);
            //         message["openId"] = Convert.ToString(target["openid"]);
            //         message["content"] = content;
            //         message["isSelf"] = isSelf;
            //         var player = AuthConnect.factory.GetPlayer(message["openId"]);
            //         if (player != null)
            //         {
            //             var instance = player.Instance;
            //             // message["user"] = new JSONObject(){ { "userId", instance["userId"] }, { "nickName", instance["nickName"] }, { "avatar", instance["avatar"] }, { "extend", instance["extend"] } };
            //             message["user"] = instance;
            //             // LogTools.Log("message player:"+instance.ToString());
            //         }

            //         LogTools.Log("message:"+channel+",target:"+message.ToString()+",content"+content);
            //         ProjectUpdate.Send(SystemMessage.ReceiveChat, message);
            //     }
            // };
        }

        public static void ChangeLocation()
        {
            var span = TimeZone.CurrentTimeZone.GetUtcOffset(System.DateTime.Now);
            AuthConnect.Location(Localization.Instance.GetLanguageAbbreviation(), span.Hours);
        }

        public static void UnAuthFlow()
        {
            LogTools.Process("[ PROJECT ] start unauth flow");
            if (AuthConnect.IsLogin)
            {
                AnalyticsManager.Event("logout");
                if (Single)
                {
                    AuthConnect.Logout();
                }
                else
                {
                    AuthConnect.Clear();
                }
            }
            CacheTools.SetVal("userToken", "");
            if (Client.IsRunning)
            {
                Client.Disconnect();
            }
        }

        public static void AuthFlow(string token = "", Action<string> callback = null)
        {
            LogTools.Process("[ PROJECT ] start auth flow ({0})", token);
            if (!Client.Connected)
            {
                LogTools.Info("[ PROJECT ] Need connect client");
                if (connectAction == null) connectAction = () => AuthFlow(token, callback);
                if (Client.IsRunning)
                {
                    Reconnect();
                    return;
                }
                Client.Connect();
                return;
            }

            if (AuthConnect.IsLogin)
            {
                LogTools.Info("[ PROJECT ] Already login success");
                return;
            }
            ChangeLocation();
            AuthConnect.App((authSuccess) =>
            {
                LogTools.Info("[ PROJECT ] App Auth:" + authSuccess);

                if (currentPage != null)
                {
                    currentPage.Send(SystemMessage.InitAppKey, "authConnect");
                }

                if (string.IsNullOrEmpty(token) && UserSetting != null)
                {
                    token = UserSetting.token;
                }

                // 使用
                if (string.IsNullOrEmpty(token))
                {
                    // 如果已经登录，直接跳转
                    token = CacheTools.GetVal("userToken", "");
                }

                LogTools.Info("[ PROJECT ] App Token:" + token);
                //                token = "80d097d4-e45b-4f13-bdd7-67e562e5e7c6";
                if (!string.IsNullOrEmpty(token))
                {
                    AnalyticsManager.Event("autoLogin");
                    LogTools.Info("[ PROJECT ] App Page:" + currentPage);
                    if (currentPage != null)
                    {
                        currentPage.Send(SystemMessage.AutoLogin, token);
                    }

                    AuthConnect.Auth(token, (GamePlayer player) =>
                    {
                        if (player == null)
                        {
                            AnalyticsManager.Event("needLogin");
                            // 通知登录框
                            if (currentPage != null)
                            {
                                currentPage.Send(SystemMessage.AppNeedLogin);
                            }

                            return;
                        }

                        // 保存该token能下次访问
                        SaveLoginToken(player, null);
                        if (callback != null) callback(token);
                    });
                }
                else
                {
                    AnalyticsManager.Event("needLogin");
                    // 通知登录框
                    if (currentPage != null)
                    {
                        currentPage.Send(SystemMessage.AppNeedLogin);
                    }
                }
            });
        }

        public static void SaveLoginToken(GamePlayer player, string nickName)
        {
            LogTools.Log("[ PROJECT ] Save Token: 1 => {0} 2 => {1}", UserSetting.token, AuthConnect.Token);
            if (UserSetting == null || UserSetting.token != AuthConnect.Token)
            {
                LogTools.Info("[ PROJECT ] Save Token:" + AuthConnect.Token);
                CacheTools.SetVal("userToken", AuthConnect.Token);
            }

            var info = player.Instance;
            if (!info["nickName"] && !string.IsNullOrEmpty(nickName))
            {
                info["nickName"] = nickName;
            }

            ProjectUpdate.LogicSuccess(player, nickName);
            StaticMethod(MainProject + ".Global", "OnLoginSuccess");
        }

        public static void LoadProject(string project, Action<bool> callback)
        {
            if (projects.Contains(project))
            {
                callback(true);
                return;
            }
            if (Resource.ILRuntime)
            {
                Game.single.StartCoroutine(Projects.Load(project, (success) =>
                {
                    if (success) projects.Add(project);
                    callback(success);
                }));
            }
            else
            {
                projects.Add(project);
                callback(true);
            }
        }

        public static void LoadBundle(BundleLoad[] list, Action<AssetBundle> callback, Action<float> notice)
        {
            var max = 0;
            var current = 0;
            foreach (var info in list)
            {
                Game.single.StartCoroutine(Resource.LoadBundle(info.Bundle, info.Variant, (i, bundle) =>
                {
                    if (info.max == -1)
                    {
                        info.max = i;
                        max += i;
                    }
                    if (i == 0)
                    {
                        current += 1;
                        if (current == list.Length)
                        {
                            callback(bundle);
                        }
                    }
                    else
                    {
                        notice((float)(max - i) / max);
                    }
                }, info.Namespace));
            }

        }

        public static object StaticMethod(string cls, string method, params object[] param)
        {
            if (Resource.ILRuntime)
            {
                return Projects.Domain.Invoke(cls, method, null, param);
            }
            else
            {
                var type = Type.GetType(cls);
                if (type != null)
                {
                    return type.InvokeMember(method,
                        System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Static
                                                                    | System.Reflection.BindingFlags.Public, null, null,
                        param);
                }
            }
            return null;
        }

        public static object Instance(string cls, params object[] param)
        {
            if (Resource.ILRuntime)
            {
                LogTools.Process("[ PROJECT ] ILRuntime:" + cls);
                return Projects.Domain.Instantiate(cls, param);
            }
            else
            {
                var type = Type.GetType(cls);
                if (type != null)
                {
                    return Activator.CreateInstance(type, param);
                }
            }
            return null;
        }

        public static void LoadEnvironmentInfo()
        {
            var environmentManifest = Path.Combine(Application.absoluteURL, environmentFile);
            if (!File.Exists(environmentManifest))
            {
                LogTools.Warn("setting is miss:{0}", environmentManifest);
                return;
            }
            else
            {
                LogTools.Process("loading setting:{0}", environmentManifest);
            }

            System.IO.StreamReader sr = new System.IO.StreamReader(environmentManifest);

            string json = sr.ReadToEnd();

            if (json.Length > 0)
            {
                EnvironmentSetting = JsonUtility.FromJson<EnvironmentSetting>(json);
            }
        }

        public static void LoadUserInfo()
        {
            var userrManifest = Path.Combine(Application.absoluteURL, userFile);
            if (!File.Exists(userrManifest))
            {
                LogTools.Warn("setting is miss:{0}", userrManifest);
                return;
            }
            else
            {
                LogTools.Process("loading setting:{0}", userrManifest);
            }

            System.IO.StreamReader sr = new System.IO.StreamReader(userrManifest);

            string json = sr.ReadToEnd();

            sr.Close();

            if (json.Length > 0)
            {
                UserSetting = JsonUtility.FromJson<UserSetting>(json);
            }
        }
    }

}